/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved.
 *
 */

import os from 'os';
import * as path from 'path';

import { cloneDeep } from '@baize/wdk';
import {
  AnalyzeModeMap,
  ContinualEvent,
  coreParameter,
  DurationEvent,
  FileSet,
  hashFile, HvigorConfigLoader,
  hvigorCore,
  MetricEventType,
  MetricLogType,
  MetricService,
  TaskDetails,
  TaskInputValue,
} from '@ohos/hvigor';
import { ProjectConfig } from '@ohos/hvigor-arkts-compose';
import { OHOS_ARK_COMPILE_SOURCE_MAP_DIR } from '@ohos/hvigor/src/common/const/const';
import fse from 'fs-extra';
import { parse } from 'ini';
import { Level } from 'log4js';
import { PackageJson } from 'type-fest';

import { findTargetProduct } from '../../common/find-target-product.js';
import { projectOhosConfigManager } from '../../common/global/project-ohos-config-manager.js';
import { BuildArtifactConst, BuildDirConst } from '../../const/build-directory-const.js';
import { CommonConst, DefaultTargetConst } from '../../const/common-const.js';
import { ApiVersion } from '../../const/sdk-const';
import { AotCompileModeEnum } from '../../enum/aot-compile-mode-enum.js';
import { CodeType } from '../../enum/code-type-enum.js';
import { CompileModeEnum } from '../../enum/compile-mode-enum.js';
import { ModuleType } from '../../enum/module-type-enum.js';
import { CodeModel } from '../../model/code-source/code-model.js';
import { CoreProjectModelImpl } from '../../model/project/core-project-model-impl.js';
import { LegacyTargetSourceSetModel } from '../../model/source-set/source-set-model.js';
import { TargetSourceSetImpl } from '../../model/source-set/target-source-set-impl.js';
import { AppOpt } from '../../options/build/app-ohos-config.js';
import { BuildOpt } from '../../options/build/build-opt.js';
import { ProjectBuildProfile } from '../../options/build/project-build-profile.js';
import { ConfigJson } from '../../options/configure/config-json-options.js';
import { ModuleJson } from '../../options/configure/module-json-options.js';
import { ArkUIXSdkInfo } from '../../sdk/info/arkuix-sdk-info.js';
import { SdkInfoExtension } from '../../sdk/sdk-info-extension.js';
import { FileUtil } from '../../utils/file-util.js';
import { InjectUtil } from '../../utils/inject-util.js';
import { getJson5Obj } from '../../utils/json-util.js';
import { ohPackageLoader } from '../../utils/loader/file/oh-package-loader.js';
import { resModelLoader } from '../../utils/loader/file/res-model-loader.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { getOhosTestIntermediatesEtsPath, isTemplatesAfterSourceCodeMigration } from '../../utils/ohos-test-util.js';
import { ModuleTargetData } from '../data/hap-task-target-data.js';
import { GlobalProjectDataService } from '../service/global-project-data-service.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';

import ConfigOptObj = ConfigJson.ConfigOptObj;

/**
 * 编译FA/Stage模型js/ets源码
 *
 * @since 2022/11/29
 */
export abstract class AbstractCompileNode extends OhosHapTask {
  private readonly TSIMPORTSENDABLE = 'ark.tsImportSendable';
  protected readonly codeType: CodeType;
  protected readonly commonOption: NodeJS.ProcessEnv = {
    path: path.dirname(process.execPath),
    watchMode: 'false',
  };
  protected readonly codeModel: CodeModel | undefined;
  protected readonly aceModuleJsonPath: string;
  protected readonly resProfileDir: string;
  protected readonly resourceTable: string;
  protected readonly rawFileResource: string;
  protected readonly aceBuildJsonDir: string;
  protected aceModuleBuild: string;
  protected readonly sourcePath: string | undefined;
  protected readonly aceSuperVisualPath: string;
  protected readonly isArkModule: boolean;
  protected readonly mainCodePath: string | undefined;
  protected readonly anBuildOutputPath: string;
  protected readonly externalApiPaths: string[] = [];
  protected buildOption?: BuildOpt;
  protected readonly isCrossplatform: boolean;
  protected userProjectConfig: object | undefined;
  protected customTypes: string[] | undefined;

  protected constructor(taskService: TargetTaskService, codeType: CodeType, taskDetails: TaskDetails) {
    super(taskService, {
      ...taskDetails,
      name: `${AbstractCompileNode.getCompileNodeTaskName(codeType, taskDetails.name!)}`,
    });
    this.buildOption = taskService.getBuildOption();
    const targetName = taskService.getTargetData().getTargetName();
    this.codeType = codeType;
    this.codeModel = this.moduleModel.getSourceSetByTargetName(targetName).getCodeMap().get(this.codeType);
    this.isArkModule = this.service.isArkModule();

    // source dir
    this.sourcePath = this.codeModel?.getSrcPath();

    if (
      this.targetName === DefaultTargetConst.OHOS_TEST_TARGET &&
      !this.isFaMode &&
      this.sourcePath &&
      isTemplatesAfterSourceCodeMigration(path.resolve(this.sourcePath!, '..'))
    ) {
      const modulePath = this.moduleModel.getModule().getNodeDir();
      this.sourcePath = getOhosTestIntermediatesEtsPath(modulePath);
    }

    this.aceSuperVisualPath = path.resolve(this.moduleModel.getSourceRootByTargetName(targetName), 'supervisual');

    // compiled module.json and some res files
    this.aceModuleJsonPath = this.pathInfo.getIntermediatesArkModuleJsonPath();
    if (!fse.existsSync(this.aceModuleJsonPath)) {
      this.aceModuleJsonPath = path.resolve(this.pathInfo.getIntermediatesProcessProfile());
    }
    this.resourceTable = path.resolve(this.pathInfo.getIntermediatesRes(), BuildArtifactConst.RESOURCE_TABLE_TXT);
    this.rawFileResource = path.resolve(this.pathInfo.getIntermediatesRes(), BuildDirConst.RESTOOL_BUILD_RESOURCES, BuildDirConst.RAW_FILE);
    this.resProfileDir = this.pathInfo.getIntermediatesResProfilePath();

    // ace config file
    this.aceBuildJsonDir = this.pathInfo.getIntermediatesLoaderPath();

    // output assets dir
    this.aceModuleBuild = path.resolve(this.pathInfo.getInterMediatesLoaderOutPath(), this.codeType);

    const sourceRoot = this.moduleModel.getSourceRootByTargetName(DefaultTargetConst.DEFAULT_TARGET);
    this.mainCodePath = path.resolve(sourceRoot, this.codeType.toString().toLowerCase());

    // anconfig file
    this.anBuildOutputPath = path.resolve(this.pathInfo.getInterMediatesLoaderOutPath(), AotCompileModeEnum.AN);
    this.isCrossplatform = this.moduleModel.isCrossplatformModule((this.projectModel as CoreProjectModelImpl).getArkUIXConfigJsonObj());
  }

  public static getCompileNodeTaskName(codeType: CodeType, taskPrefix: string) {
    if (codeType === CodeType.ETS) {
      return `${taskPrefix}ArkTS`;
    }
    return `${taskPrefix}${codeType.toUpperCase()}`;
  }

  protected collectCustomTypes(): string[] {
    return this.targetService.getCustomTypes() || [];
  }


  async beforeAlwaysAction() {
    // 增量阶段之前，收集rollup需要用到的用户配置项
    // 注意：该对象配置后，会用于本任务增量以及rollup transform任务增量，请谨慎设置
    this.userProjectConfig = {
      autoLazyImport: this.targetService.getBuildOption().arkOptions?.autoLazyImport ?? false,
      allowEmptyBundleName: hvigorCore.getParameter().getProperty(CommonConst.EMPTY_BUNDLENAME) === true,
      reExportCheckMode: this.targetService.getBuildOption().arkOptions?.reExportCheckMode ?? 'noCheck',
      executionMode: coreParameter.startParams.optimizationStrategy,
      usePathPlaceholder: HvigorConfigLoader.getInstance().getPropertiesConfigValue(CommonConst.OHOS_ROLLUP_CACHE_USE_PATH_PLACEHOLDER) === true,
    };

    this.customTypes = this.collectCustomTypes();
  }

  protected get isByteCodeHarOptimize () {
    return InjectUtil.isByteCodeHarOptimize();
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputsMap = new Map<string, TaskInputValue>()
      .set('debuggable', this.targetService.isDebug())
      .set('isArk', this.isArkModule)
      .set('needCoverageInsert', InjectUtil.isOhosTestCoverage())
      .set(this.TSIMPORTSENDABLE, hvigorCore.getParameter().getProperty(this.TSIMPORTSENDABLE) === true)
      .set('customTypes', this.customTypes ?? '')
      .set('isByteCodeHarOptimize', this.isByteCodeHarOptimize);

    for (const [pkgName, pkgPath] of Object.entries((this.packageJsonObj as PackageJson).dependencies ?? {})) {
      inputsMap.set(pkgName, `${pkgName}: ${pkgPath}`);
    }

    if (this.moduleModel.getModuleType() === ModuleType.Har && !this.buildOption?.debuggable) {
      inputsMap.set('sourceMapDir', coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR] ?? '');
    }

    // 适配5.1，新增har支持配置分支消除
    // 在release模式下，只要设置了branchElimination为true，则在生成abc时进行裁剪
    if (!this.buildOption?.debuggable) {
      inputsMap.set('branchElimination', this.targetService.getBuildOption()?.arkOptions?.branchElimination ?? false);
    }

    inputsMap.set('caseSensitiveCheck', this.targetService.getBuildOption()?.strictMode?.caseSensitiveCheck ?? false);

    inputsMap.set(CommonConst.USE_NORMALIZED_OHM_URL, this.getUseNormalizedOHMUrl());

    inputsMap.set(BuildDirConst.TRANSFORM_LIB, this.targetService.getBuildOption()?.arkOptions?.transformLib ?? '');

    inputsMap.set('compatibleSdkVersionStage', this.compatibleSdkVersionStage || '');

    inputsMap.set('skipOhModulesLint', this.targetService.getBuildOption()?.arkOptions?.skipOhModulesLint === true);

    if (this.userProjectConfig){
      Object.entries(this.userProjectConfig).forEach(([key, value]) => {
        inputsMap.set(key, value);
      });
    }

    return inputsMap;
  }

  declareOutputFiles(): FileSet {
    const fileSet = new FileSet().addEntry(this.aceModuleBuild, { isDirectory: true });
    if (this.targetService.getAnBuildMode() === AotCompileModeEnum.AOT_PARTIAL) {
      fileSet.addEntry(this.anBuildOutputPath, { isDirectory: true });
    }

    if (this.moduleModel.getModuleType() === ModuleType.Har && !this.buildOption?.debuggable) {
      if (coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]) {
        const sourceMapPath = this.getSourceMapDir(coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]);
        fileSet.addEntry(path.resolve(sourceMapPath, BuildArtifactConst.SOURCEMAPS_MAP), { isDirectory: false });
      } else {
        fileSet.addEntry(path.resolve(this.pathInfo.getDefaultSourceMapPath(), BuildArtifactConst.SOURCEMAPS_MAP), { isDirectory: false });
      }
    }
    return fileSet;
  }

  declareInputFiles(): FileSet {
    const fileSet = new FileSet()
      .addEntry(this.aceBuildJsonDir, { isDirectory: true })
      .addEntries(this.getAllExistDependentMainPath())
      .addEntries(this.getAllExistHarSrcPath(), { isDirectory: true });
    if (fse.existsSync(this.rawFileResource)) {
      fileSet.addEntry(this.rawFileResource, { isDirectory: true });
    }
    const resourceDirs: string[] = this.targetService.getResourceDirs();
    if (resourceDirs.length > 0) {
      fileSet.addEntry(this.resourceTable);
    }
    if (!this.service.hasLiteDevice()) {
      fileSet.addEntry(this.aceModuleJsonPath);
    }

    if (this.resProfileDir && fse.existsSync(this.resProfileDir)) {
      fileSet.addEntry(this.resProfileDir, { isDirectory: true });
    }

    if (this.aceSuperVisualPath && fse.existsSync(this.aceSuperVisualPath)) {
      fileSet.addEntry(this.aceSuperVisualPath, { isDirectory: true });
    }
    if (this.sourcePath && fse.existsSync(this.sourcePath)) {
      fileSet.addEntry(this.sourcePath, { isDirectory: true });
    }

    // 添加入口Index 如果路径不存在或路径不是一个文件则不添加增量判断
    const pkgJsonOpt: PackageJson = getJson5Obj(this.packageManagerPath);
    if (pkgJsonOpt) {
      const indexFilePath = path.join(this.module.getNodeDir(), pkgJsonOpt.main || 'Index.ets');
      if (fse.existsSync(indexFilePath) && fse.statSync(indexFilePath).isFile()) {
        fileSet.addEntry(indexFilePath);
      }
    }

    if (this.targetService.getTargetData().getTargetName() === CommonConst.OHOS_TEST) {
      const ohosTestPath = path.resolve(this.module.getNodeDir(), BuildDirConst.SRC, CommonConst.OHOS_TEST);
      if (fse.existsSync(ohosTestPath)) {
        fileSet.addEntry(ohosTestPath, { isDirectory: true });
      }
    }

    if (fse.existsSync(this.pathInfo.getIntermediatesPkgContextInfoPath())) {
      fileSet.addEntry(this.pathInfo.getIntermediatesPkgContextInfoPath());
    }

    return fileSet;
  }

  protected getAllExistHarSrcPath(): string[] {
    return this.service
      .getDependencyRootPaths()
      .map((harPath) => path.resolve(harPath, 'src', 'main', this.codeType))
      .filter((harPath) => fse.existsSync(harPath));
  }

  protected getAllExistDependentMainPath(): string[] {
    return this.service.getDependencyMainPaths().filter((mainPath) => fse.existsSync(mainPath));
  }

  taskShouldDo(): boolean {
    return this.sourcePath !== undefined;
  }

  /**
   * 移动_releaseMap到临时目录
   *
   * @param {ModuleTargetData} targetData
   * @param log 日志
   * @param {string} abilityPath 老模型的_releaseMap需要会拷贝一级目录到abilityPath
   * @protected
   */
  protected moveReleaseMap(targetData: ModuleTargetData, log: OhosLogger, abilityPath: string = this.codeType): void {
    const reMapDir: string = path.resolve(targetData.getPathInfo().getInterMediatesLoaderOutPath(), abilityPath, BuildArtifactConst.RELEASE_MAP);
    if (!fse.existsSync(reMapDir)) {
      return;
    }

    const tempDir: string = path.resolve(this.getTaskTempDir(targetData), abilityPath, BuildArtifactConst.RELEASE_MAP);
    if (fse.existsSync(tempDir)) {
      fse.removeSync(tempDir);
    }
    fse.moveSync(reMapDir, tempDir);
    log.debug(`move ${reMapDir} to ${tempDir}`);
  }

  protected async beforeTask() {
    if (!InjectUtil.isColdReload()) {
      fse.removeSync(this.aceModuleBuild);
    }
    if ((this.projectModel as CoreProjectModelImpl).isCrossplatformProject()) {
      const apiMeta: ProjectBuildProfile.ApiMeta = this.projectModel!.getCompileApiMetaByProduct(findTargetProduct(this.projectModel!).name);
      const arkUIXInfo: ArkUIXSdkInfo = new SdkInfoExtension(apiMeta).getArkUIXInfo();
      await arkUIXInfo.setup();
      const arkUIXRootPath = arkUIXInfo.getArkUIXRootPath();
      this.externalApiPaths.push(path.resolve(arkUIXRootPath));
    }
  }

  /**
   * 工具链的临时目录根据debug维度和compileMode维度在区分一层
   *
   * @param {ModuleTargetData} targetData
   * @param {string} appendStr 默认为'', 区分不同产物的不同目录
   * @returns {string}
   * @protected
   */
  protected getTaskTempDir(targetData: ModuleTargetData, appendStr = ''): string {
    const nodeTempDir = path.resolve(
      this.getArkCompileCachePath(targetData),
      `${this.targetService.getBuildMode().toLowerCase()}${appendStr}`,
    );
    if (!fse.existsSync(nodeTempDir)) {
      fse.mkdirsSync(nodeTempDir);
    }
    return nodeTempDir;
  }

  /**
   * 工具链的临时目录根据compileMode维度区分
   *
   * @param {ModuleTargetData} targetData
   * @returns {string}
   * @protected
   */
  protected getArkCompileCachePath(targetData: ModuleTargetData): string {
    const nodeTempDir = path.resolve(
      super.getTaskTempDir(targetData),
      `${this.targetCompileMode}`
    );
    if (!fse.existsSync(nodeTempDir)) {
      fse.mkdirsSync(nodeTempDir);
    }
    return nodeTempDir;
  }

  protected validateModuleJson(logger: OhosLogger): void {
    const moduleForArkJsonPath = this.pathInfo.getIntermediatesArkModuleJsonPath();
    const moduleJsonObj: ModuleJson.ModuleOptObj = getJson5Obj(moduleForArkJsonPath);
    this.validateModulePage(logger, moduleJsonObj);
    this.validateAbilitiesAndExtensionAbilitiesSrcEntry(logger, moduleJsonObj);
  }

  protected initCommonArkConfig(): any {
    const appOptFromConfig: AppOpt = cloneDeep(projectOhosConfigManager.getOverrides()?.appOpt);
    const pkgContextInfo = this.getPkgContextInfo();
    let resolveConflictMode: boolean = this.getResolveConflictMode();
    let name2RootPath = new Map<string, string>();
    if (resolveConflictMode) {
      const res = this.service.getDependencyName2RootPath();
      if (res === false) {
        resolveConflictMode = false;
        name2RootPath = new Map<string, string>();
      } else {
        name2RootPath = res;
      }
    }
    const newStrictMode = this.buildOption?.strictMode;
    if (newStrictMode?.useNormalizedOHMUrl) {
      if (newStrictMode?.noExternalImportByPath === undefined) {
        // useNormalizedOHMUrl=true时noExternalImportByPath若无配置则默认为true
        Object.assign(newStrictMode, { noExternalImportByPath: true });
      }
    }
    const etsLoaderPath = fse.realpathSync.native(this.sdkInfo.getEtsLoader());
    return {
      moduleType: this.moduleModel.getModuleType(),
      perf: AnalyzeModeMap.get(hvigorCore.getParameter().getStartParams().analyze),
      targetName: `.${this.targetName}`,
      packageManagerType: this.isOhpmProject ? 'ohpm' : 'npm',
      localPropertiesPath: path.resolve(this.projectModel.getProjectDir(), 'local.properties'),
      isPreview: false,
      isOhosTest: this.targetName === 'ohosTest',
      isLocalTest: InjectUtil.isLocalTest(),
      buildMode: this.targetService.getBuildMode(),
      hotReloadBuild: hvigorCore.getParameter().getStartParams().hotReloadBuild,
      watchMode: 'false',
      aceProfilePath: this.resProfileDir,
      etsLoaderPath: etsLoaderPath,
      modulePath: this.module.getNodeDir(),
      testFrameworkPar: {
        testMode: InjectUtil.isOhosTestCoverage() ? CommonConst.OHOS_TEST : undefined,
        coveragePathFilter: InjectUtil.getCoveragePathFilter(),
        coverageMode: InjectUtil.getCoverageMode(),
      },
      needCoverageInsert: InjectUtil.isOhosTestCoverage(),
      debugLine: InjectUtil.isDebugLineEnable(),
      projectTopDir: path.resolve(this.projectModel.getProjectDir()),
      compileSdkVersion: this.compileApiVersion,
      compatibleSdkVersion: this.compatibleApiVersion,
      compatibleSdkVersionStage: this.compatibleSdkVersionStage,
      bundleName: appOptFromConfig?.bundleName ?? this.targetData.getProduct().bundleName ?? this.projectModel.getDefaultBundleName(),
      etsLoaderVersion: this.sdkInfo.getEtsComponentVersion(),
      etsLoaderReleaseType: this.sdkInfo.getEtsComponentReleaseType(),
      aotCompileMode: InjectUtil.isOhosTestCoverage() ? AotCompileModeEnum.AOT_NULL : this.targetService.getAnBuildMode(),
      apPath: this.targetService.getApAbsolutePath(),
      entryModuleName: this.moduleName,
      entryModuleVersion: this.packageJsonObj.version,
      // 测试框架要求编ohosTest的覆盖率包时添加1个 _test 后缀
      entryPackageName: this.targetName === 'ohosTest' && InjectUtil.isOhosTestCoverage() ? `${this.packageJsonObj.name}_test` : this.packageJsonObj.name,
      allModuleNameHash: GlobalProjectDataService.getInstance().getAllModuleNameHash(),
      externalApiPaths: this.getExternalApiPaths(),
      compilerTypes: this.customTypes,
      isCrossplatform: this.isCrossplatform,
      hvigorPluginFile: this.getHvigorPluginFile(),
      compilePluginPath: this.moduleModel.getCompilePluginPath(),
      buildGeneratedProfilePath: path.resolve(this.pathInfo.getGenerateBuildProfileDir(), this.targetName),
      bundleType: appOptFromConfig?.bundleType ?? this.targetData.getProduct().bundleType ?? this.projectModel.getBundleType(),
      arkTSVersion: this.targetData.getProduct().arkTSVersion,
      apiVersion: this.sdkInfo.getSdkVersion(),
      needCompleteSourcesMap:
        this.targetName === 'ohosTest' ||
        InjectUtil.isOhosTestCoverage() ||
        InjectUtil.isLocalTest() ||
        (this.moduleModel.isHarModule() && !this.targetService.isOpenSource()),
      isFaMode: this.isFaMode,
      strictMode: newStrictMode,
      buildDir: this.pathInfo.getModuleBuildPath(),
      deviceTypes: this.moduleModel.getDeviceTypes(),
      useNormalizedOHMUrl: this.getUseNormalizedOHMUrl(),
      pkgContextInfo,
      ohPackagePathMap: ohPackageLoader.OhPackagePathMap,
      dependencyAliasMap: this.getDependencyAliasMap(pkgContextInfo),
      permission: this.moduleModel.getPermission(),
      integratedHsp: this.moduleModel.isHspModule() ? this.targetService.getBuildOption().arkOptions?.integratedHsp : false,
      projectArkOption: this.targetService.getBuildOption()?.arkOptions,
      sourceMapDir: coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]
        ? this.getSourceMapDir(coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR])
        : undefined,
      branchElimination: this.targetService.getBuildOption().arkOptions?.branchElimination ?? false,
      transformLib: this.normalizeTransformLib(),
      caseSensitiveCheck: this.targetService.getBuildOption()?.strictMode?.caseSensitiveCheck ?? false,
      tsImportSendable: hvigorCore.getParameter().getProperty(this.TSIMPORTSENDABLE) === true,
      resolveConflictMode,
      depName2RootPath: name2RootPath,
      depName2DepInfo: this.service.getDependencyName2DepInfo(),
      rootPathSet: Array.from(GlobalProjectDataService.getInstance().getRootPathSet()),
      useNativeResolver: hvigorCore.getParameter().getProperty(CommonConst.USE_NATIVE_RESOLVER) ?? true,
      // 1.js_bundle需要emit js 2.代码覆盖率插桩需要 emit js 3.默认情况 emit js 除非配置了 NO_EMIT_JS === true
      shouldEmitJs:
        this.targetCompileMode === CompileModeEnum.JS_BUNDLE ||
        InjectUtil.isOhosTestCoverage() ||
        !(hvigorCore.getParameter().getProperty(CommonConst.NO_EMIT_JS) === true && this.checkSdkTscSyntaxKind(etsLoaderPath)),
      // 默认关闭，除非显示配置了 true 才开启
      useTscResolve: hvigorCore.getParameter().getProperty(CommonConst.USE_TSC_RESOLVE) === true,
      singleFileEmit:
        this.targetService.isDebug() && (hvigorCore.getParameter().getProperty(CommonConst.SINGLE_FILE_EMIT) ?? false) && !InjectUtil.isOhosTestCoverage(),
      userProjectConfig: this.userProjectConfig,
      skipOhModulesLint: this.targetService.getBuildOption().arkOptions?.skipOhModulesLint === true,
      executionMode: coreParameter.startParams.optimizationStrategy,
      ...this.userProjectConfig,
      arkCompileCachePath: this.getArkCompileCachePath(this.targetData)
    };
  }

  /**
   * 检查 sdk 的 tsc 里面是否有导出的 SyntaxKind 枚举类， 因为 noEmitJs TS-AST转换方案依赖 SyntaxKind，如果没有就不使能 noEmitJs 方案。
   */
  private checkSdkTscSyntaxKind(etsLoaderPath: string): boolean {
    try {
      const ts = require(path.join(etsLoaderPath, 'node_modules', 'typescript'));
      const syntaxKind = ts.SyntaxKind;
      if (!syntaxKind) {
        return false;
      }
      if (!syntaxKind.ImportDeclaration || !syntaxKind.ExportDeclaration || !syntaxKind.CallExpression || !syntaxKind.ImportEqualsDeclaration) {
        return false;
      }
      return true;
    } catch (e: any) {
      return false;
    }
  }

  private getResolveConflictMode(): boolean {
    const parseOhpmrc = (ohpmrcPath: string) => {
      if (fse.existsSync(ohpmrcPath)) {
        const ohpmrc = parse(fse.readFileSync(ohpmrcPath, { encoding: 'utf-8' }));
        return ohpmrc.resolve_conflict;
      }
      return undefined;
    };
    let res = parseOhpmrc(path.resolve(this.projectModel.getProjectDir(), '.ohpmrc'));
    if (res !== undefined) {
      return res;
    }
    res = parseOhpmrc(path.resolve(os.homedir(), '.ohpm/.ohpmrc'));
    if (res !== undefined) {
      return res;
    }
    return true;
  }

  /**
   * 处理transformLib 字段
   */
  protected normalizeTransformLib() {
    if (!InjectUtil.isInTest() && this.targetService.isSourceCodeHar()) {
      // 源码har不处理返回undefined
      return undefined;
    }

    // hap/hsp/byteCoderHar或当在localTest或ohosTest流程中时则需要继续处理
    const transformLib = this.targetService.getBuildOption()?.arkOptions?.transformLib;
    if (!transformLib) {
      return undefined;
    }
    return path.resolve(this.pathInfo.getModulePath(), transformLib);
  }

  /**
   * 获取HAP/HSP编译期间需要被编译阻断的依赖
   * @protected
   */
  protected getBlockDependencies() {
    return this.isByteCodeHarOptimize ? this.service.getRootHspDependencies().concat(this.service.getRootByteCodeHarDependencies()) : this.service.getHspOrByteCodeHarDependencies();
  }

  /**
   * 初始化默认的ark编译参数配置
   *
   * @returns {ProjectConfig} ark编译参数
   * @protected
   */
  protected async initDefaultArkCompileConfig(): Promise<ProjectConfig> {
    const hasDeclaredFiles = this.moduleModel.isHspModule() || this.targetService.isByteCodeHar();
    const declaredFilesPath = this.pathInfo.getIntermediatesEtsTgzPath(this.moduleName);
    return {
      ...this.initCommonArkConfig(),
      aceModuleJsonPath: this.aceModuleJsonPath,
      appResource: this.resourceTable,
      rawFileResource: this.rawFileResource,
      resourceTableHash: hashFile(this.resourceTable),
      runtimeOS: this.sdkInfo.isOhos ? CommonConst.OPEN_HARMONY : CommonConst.HARMONY_OS,
      sdkInfo:
        `${this.sdkInfo.isOhos}:${this.sdkInfo.getSdkVersion()}:` + `${this.sdkInfo.getEtsComponentVersion()}:${this.sdkInfo.getEtsComponentReleaseType()}`,
      aceModuleRoot: this.sourcePath!,
      compileMode: this.targetCompileMode,
      aceSuperVisualPath: this.aceSuperVisualPath,
      aceBuildJson: path.resolve(this.aceBuildJsonDir, BuildArtifactConst.LOADER_JSON),
      cachePath: this.getTaskTempDir(this.targetData),
      aceModuleBuild: this.aceModuleBuild,
      supportChunks: true,
      declaredFilesPath: hasDeclaredFiles ? declaredFilesPath : undefined,
      shouldCollectDirectImportees: this.isByteCodeHarOptimize,
      isByteCodeHarOptimize: this.isByteCodeHarOptimize
    };
  }

  protected async getPreviewCompileConfig(logger: OhosLogger) {
    const buildConfigPath: string = this.pathInfo.getBuildConfigPath(true);
    let compileConfig: any = {};
    if (fse.existsSync(buildConfigPath)) {
      compileConfig = fse.readJsonSync(buildConfigPath);
    } else {
      logger.debug(`Build config file not found: ${buildConfigPath}`);
    }

    // 在预览场景不使用传入的cache，而是使用有product和target的、基于buildRoot的默认路径
    if (compileConfig.cachePath) {
      delete compileConfig.cachePath;
    }
    const config: ProjectConfig = {
      ...(await this.initDefaultArkCompileConfig()),
      ...compileConfig,
    };
    if (config.obfuscationOptions) {
      delete config.obfuscationOptions;
    }

    config.watchMode = 'true';
    config.isPreview = true;
    config.projectPath = config.aceModuleRoot;
    config.level = logger.getLevel() as Level;
    config.buildDir = this.pathInfo.getModulePreviewPath();
    config.sdkPath = this.sdkInfo.sdkDir;
    fse.ensureDirSync(config.cachePath!);
    fse.ensureDirSync(config.aceModuleBuild!);
    return config;
  }

  /**
   * 处理hvigor-compiler回传的compile event事件
   *
   * @param compileEvents 回传的compile event事件
   * @param parentEvent compile event事件对应的父事件
   * @param needUpdateTid 是否需要将子节点的tid更新为父节点的tid
   * @protected
   */
  protected handleCompileEvents(compileEvents: any[], parentEvent: DurationEvent | undefined, needUpdateTid: boolean) {
    if (!parentEvent || !compileEvents) {
      return;
    }
    compileEvents.forEach((compileEvent) => {
      const event: DurationEvent | ContinualEvent = Object.assign(
        compileEvent.head.type === MetricEventType.DURATION ? new DurationEvent('', '', '', 0, '', '') : new ContinualEvent('', '', '', 0, ''),
        compileEvent,
      );
      MetricService.getInstance().submit(event);
      if (!event.getParent()) {
        parentEvent.addChild(event.getId());
        event.setParent(parentEvent.getId());
      }
      event.setLog(event.getName(), MetricLogType.INFO, event.getDescription());
    });
    if (needUpdateTid) {
      this.updateTid(parentEvent);
    }
  }

  protected validateAbility(logger: OhosLogger): void {
    const targetName = this.targetData.getTargetName();
    const sourceSetModel = <LegacyTargetSourceSetModel>this.moduleModel.getSourceSetByTargetName(targetName);
    const configJsonPath: string = sourceSetModel.getLegacyModuleTargetRes().getJsonPath();
    if (!fse.existsSync(configJsonPath)) {
      return;
    }
    const configJsonObj: ConfigOptObj = resModelLoader.getConfigJson(configJsonPath);
    const abilityObjs: ConfigJson.AbilitiesObj[] = configJsonObj.module.abilities ? configJsonObj.module.abilities : [];
    const moduleSrcRootPath = path.resolve(
      this.moduleModel.getModule().getNodeDir(),
      BuildDirConst.SRC,
      targetName === CommonConst.OHOS_TEST ? CommonConst.OHOS_TEST : BuildDirConst.MAIN,
    );
    const abilityMap = new Map<string, string>();
    abilityObjs.forEach((abilityObj) => {
      if (!abilityObj.srcLanguage) {
        logger.printErrorExit('SRC_LANGUAGE_UNDEFINED', [abilityObj.name, configJsonObj]);
      }
      const abilitySrcLanguage: string = abilityObj.srcLanguage ? abilityObj.srcLanguage : '';
      const abilitySrcPath = path.resolve(moduleSrcRootPath, abilitySrcLanguage, abilityObj.srcPath);
      if (!fse.existsSync(abilitySrcPath)) {
        logger.printErrorExit('SRC_PATH_NOT_FOUND', [abilityObj.srcPath, abilitySrcPath, configJsonPath], [[abilityObj.srcPath]]);
      }
      abilityMap.set(abilityObj.name, abilitySrcLanguage);
    });

    const jsObjs: ConfigJson.JsObj[] = configJsonObj.module.js ? configJsonObj.module.js : [];
    jsObjs.forEach((jsObj) => {
      if (jsObj.type === 'form') {
        return;
      }

      const srcLanguage: string = abilityMap.get(jsObj.name) ?? '';
      jsObj.pages.forEach((jsObjPage) => {
        const pageAbilityPath = path.resolve(moduleSrcRootPath, srcLanguage, `${jsObj.name.substring(1)}`, `${jsObjPage}.${jsObj.mode?.syntax ?? 'hml'}`);
        if (!fse.existsSync(pageAbilityPath)) {
          logger.printErrorExit('MODULE_JS_PAGE_NOT_FOUND', [jsObjPage, pageAbilityPath, configJsonPath], [[jsObjPage]]);
        }
      });
    });
  }

  /**
   * 以parentEvent为根节点，将其所有的子节点的tid更新为根节点的tid
   *
   * @param parentEvent 根节点
   * @private
   */
  private updateTid(parentEvent: DurationEvent) {
    parentEvent.getChildren().forEach((child) => {
      const childEvent = MetricService.getInstance().getEventById(child);
      if (childEvent) {
        this.updateTid(childEvent.setTid(parentEvent.getTid()) as DurationEvent);
      }
    });
  }

  private validateModulePage(logger: OhosLogger, moduleJsonObj: ModuleJson.ModuleOptObj): void {
    // $profile:xxxxxx
    const pagesStr: string = moduleJsonObj.module.pages;
    if (pagesStr === undefined) {
      return;
    }
    const pageJsonFileName = `${pagesStr.replace(/\$profile:/, '')}.json`;
    const pageJsonFilePath: string = path.resolve(this.resProfileDir, pageJsonFileName);
    if (!fse.existsSync(pageJsonFilePath)) {
      logger.printErrorExit('MODULE_PAGE_NOT_FOUND', [pagesStr,pageJsonFilePath],[[pageJsonFileName]]);
    }
  }

  private validateAbilitiesAndExtensionAbilitiesSrcEntry(logger: OhosLogger, moduleJsonObj: ModuleJson.ModuleOptObj){
    this.validateSrcEntry(logger, moduleJsonObj.module.abilities);
    this.validateSrcEntry(logger, moduleJsonObj.module.extensionAbilities);
  }

  private validateSrcEntry(logger: OhosLogger, moduleAbilities: ModuleJson.AbilityObj[] | undefined): void {
    const sourceSetModel = this.moduleModel.getSourceSetByTargetName(this.targetData.getTargetName());
    if (!moduleAbilities || (moduleAbilities && moduleAbilities.length === 0)) {
      return;
    }
    for (const abilityItem of moduleAbilities) {
      const srcEntry = abilityItem.srcEntry;
      if (srcEntry === undefined) {
        continue;
      }
      const srcEntryPath = path.isAbsolute(srcEntry) ? srcEntry : path.resolve(sourceSetModel.getSourceSetRoot(), srcEntry);

      if (this.targetName === DefaultTargetConst.OHOS_TEST_TARGET && isTemplatesAfterSourceCodeMigration(sourceSetModel.getSourceSetRoot())) {
        return;
      }

      if (!FileUtil.fileExists(srcEntryPath)) {
        const targetSourceSet = sourceSetModel as TargetSourceSetImpl;
        logger.printErrorExit('MODULE_SRC_ENTRY_NOT_FOUND', [srcEntry, targetSourceSet.getModuleTargetRes().getJsonPath()]);
      }
    }
  }

  private getHvigorPluginFile(): string | undefined {
    let hvigorPluginFile = this.buildOption?.arkOptions?.compilePluginFile;
    if (hvigorPluginFile) {
      const completePath = path.resolve(this.moduleModel.getProjectDir(), hvigorPluginFile);
      hvigorPluginFile = fse.existsSync(completePath) ? completePath : `${completePath}.ts`;
    }
    return hvigorPluginFile;
  }

  private getExternalApiPaths() {
    if (this.compileApiVersion > ApiVersion.API_VERSION_9 && this.targetData.isHarmonyOS() && this.sdkInfo.getHmsArkDir()) {
      this.externalApiPaths.push(this.sdkInfo.getHmsArkDir()!);
    }
    return this.externalApiPaths;
  }

  protected getUseNormalizedOHMUrl() {
    return !!this.buildOption?.strictMode?.useNormalizedOHMUrl;
  }

  private getPkgContextInfo() {
    const pkgInfoPath = this.pathInfo.getIntermediatesPkgContextInfoPath();
    return getJson5Obj(pkgInfoPath);
  }

  private getDependencyAliasMap(pkgContextInfo: any) {
    const dependencyAliasMap = new Map<string, string>();
    if (!pkgContextInfo) {
      return dependencyAliasMap;
    }
    Object.values(pkgContextInfo).forEach((contextInfo: any) => {
      if (contextInfo.dependencyAlias) {
        dependencyAliasMap.set(contextInfo.dependencyAlias, contextInfo.packageName);
      }
    });
    return dependencyAliasMap;
  }

  private getSourceMapDir(sourceMapPath: string) {
    if (path.isAbsolute(sourceMapPath)) {
      return path.resolve(coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]!, this.moduleName);
    }
    return path.resolve(this.projectModel.getProjectDir(), 'hvigor', coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]!, this.moduleName);
  }
}
